#include <iostream>
#include <thread>

#include <tbox/base/log.h>
#include <tbox/log/async_file_sink.h>
#include <tbox/log/async_stdout_sink.h>

#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include <spdlog/async.h>

using namespace std;

tbox::log::AsyncStdoutSink stdout_log;
tbox::log::AsyncFileSink   file_log;

#define ENABLE_CONSOLE
#define ENABLE_FILE

void InitSpdlog() {
  spdlog::init_thread_pool(100 << 10, 1);

  std::vector<std::shared_ptr<spdlog::sinks::sink>> sinks_vec;
#ifdef ENABLE_CONSOLE
  auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
  sinks_vec.push_back(console_sink);
#endif

#ifdef ENABLE_FILE
  auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("/tmp/log/spdlog.log", 2 * 1024 * 1024, 20);
  sinks_vec.push_back(file_sink);
#endif

  //auto default_logger = std::make_shared<spdlog::logger>("demo", sinks_vec.begin(), sinks_vec.end());
  auto default_logger = std::make_shared<spdlog::async_logger>("demo", sinks_vec.begin(), sinks_vec.end(),
                                                               spdlog::thread_pool(), spdlog::async_overflow_policy::block);
  default_logger->set_pattern("%^%L %m-%d %H:%M:%S.%f %6t %n %v%$");
  spdlog::set_default_logger(default_logger);
}

void InitTboxLog() {
  file_log.setFilePath("/tmp/log/");
  file_log.setFilePrefix("tbox");
  stdout_log.enableColor(true);
}

std::chrono::nanoseconds PrintTboxLog(int i) {
    auto start_time_point = std::chrono::steady_clock::now();

    LogInfo("tbox count:%d", i);
    LogInfo("this is tbox, count:%d", i);
    LogInfo("this is tbox, xxxxxxxxxxxxxxxxxxxxxx count:%d", i);
    LogInfo("this is tbox, xxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy count:%d", i);
    LogInfo("this is tbox, xxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz count:%d", i);

    LogInfo("tbox count:%d", i);
    LogInfo("this is tbox, count:%d", i);
    LogInfo("this is tbox, xxxxxxxxxxxxxxxxxxxxxx count:%d", i);
    LogInfo("this is tbox, xxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy count:%d", i);
    LogInfo("tbox count:%d", i);

    return std::chrono::steady_clock::now() - start_time_point;
}

std::chrono::nanoseconds PrintSpdLog(int i) {
    auto start_time_point = std::chrono::steady_clock::now();

    spdlog::info("{}() spd  count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);
    spdlog::info("{}() this is spd , count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);
    spdlog::info("{}() this is spd , xxxxxxxxxxxxxxxxxxxxxx count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);
    spdlog::info("{}() this is spd , xxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);
    spdlog::info("{}() this is spd , xxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);

    spdlog::info("{}() spd  count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);
    spdlog::info("{}() this is spd , count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);
    spdlog::info("{}() this is spd , xxxxxxxxxxxxxxxxxxxxxx count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);
    spdlog::info("{}() this is spd , xxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);
    spdlog::info("{}() spd  count:{} -- {}:{}", __func__, i, __FILE__, __LINE__);

    return std::chrono::steady_clock::now() - start_time_point;
}

void TestTimeCost(std::chrono::nanoseconds interval_time) {
  cerr << "== test: interval_time " << interval_time.count() << " ns ==" << endl;
#ifdef ENABLE_FILE
  file_log.enable();
#endif
#ifdef ENABLE_CONSOLE
  stdout_log.enable();
#endif

  constexpr long COUNT = 10000;

  std::chrono::nanoseconds tbox_cost_acc = std::chrono::nanoseconds::zero();
  std::chrono::nanoseconds spd_cost_acc = std::chrono::nanoseconds::zero();

  for (int i = 0; i < COUNT; ++i) {
    tbox_cost_acc += PrintTboxLog(i);
    spd_cost_acc += PrintSpdLog(i);
    std::this_thread::sleep_for(interval_time);
  }

  auto tbox_avg_nano_count = tbox_cost_acc.count() / (COUNT * 10);
  auto spd_avg_nano_count = spd_cost_acc.count() / (COUNT * 10);

#ifdef ENABLE_FILE
  file_log.disable();
#endif
#ifdef ENABLE_CONSOLE
  stdout_log.disable();
#endif

  cerr << "tbox: " << tbox_avg_nano_count << " ns , " << tbox_cost_acc.count() << " ns"<< endl;
  cerr << "spd : " << spd_avg_nano_count << " ns , " << spd_cost_acc.count() << " ns"<< endl;
  cerr << "tbox/spd : " << tbox_cost_acc.count() * 1.0 / spd_cost_acc.count() << endl;
}

void TestThroughput() {
  cerr << "== test throughput ==" << endl;
#ifdef ENABLE_FILE
  file_log.enable();
#endif
#ifdef ENABLE_CONSOLE
  stdout_log.enable();
#endif

  constexpr long COUNT = 1000;

  auto start_time_point = std::chrono::steady_clock::now();
  for (int i = 0; i < COUNT; ++i)
    PrintTboxLog(i);

#ifdef ENABLE_FILE
  file_log.disable();
#endif
#ifdef ENABLE_CONSOLE
  stdout_log.disable();
#endif

  auto tbox_cost_acc = std::chrono::steady_clock::now() - start_time_point;

  start_time_point = std::chrono::steady_clock::now();
  for (int i = 0; i < COUNT; ++i)
    PrintSpdLog(i);
  spdlog::shutdown();
  auto spd_cost_acc = std::chrono::steady_clock::now() - start_time_point;

  cerr << "tbox: " << tbox_cost_acc.count() << " ns" << endl;
  cerr << "spd : " << spd_cost_acc.count() << " ns" << endl;
  cerr << "tbox/spd : " << tbox_cost_acc.count() * 1.0 / spd_cost_acc.count() << endl;
}

int main() {
  InitSpdlog();
  InitTboxLog();

#if 1
  for (int i = 0; i < 1000; i+=100)
    TestTimeCost(std::chrono::nanoseconds(i));
  for (int i = 1; i < 1000; i+=100)
    TestTimeCost(std::chrono::microseconds(i));
#endif

  TestThroughput();

  return 0;
}

